#include <stdlib.h>
#include <stdint.h>
#include "mt64.h"
#include "common.h"
#include "bitboard/bitboard.h"
#include "bitboard/bitboard_init.h"
#include "bitboard/bitboard_movelist.h"
#include "chess.h"
#include "hash.h"

uint64_t hash_number_piece_position[12][64];
uint64_t hash_number_side_to_move;
uint64_t hash_number_castle_rights[4];
uint64_t hash_number_enpassant[8];

const int piece_to_hash_piece[8] = {0, 0, 1, 2, 0, 3, 4, 5};

void init_hash_numbers(uint64_t seed)
{
	int piece, i;

	init_genrand64(seed);

	for (piece = 0; piece < 12; piece++) {
		for (i = 0; i < 64; i++) {
			hash_number_piece_position[piece][i] = genrand64_int64();
		}
	}

	for (i = 0; i < 4; i++) {
		hash_number_castle_rights[i] = genrand64_int64();
	}


	for (i = 0; i < 8; i++) {
		hash_number_enpassant[i] = genrand64_int64();
	}

	hash_number_side_to_move = genrand64_int64();
}

ttable_t create_ttable(int index_bit_length)
{
	ttable_t ttable;

	ttable.index_bit_length = index_bit_length;
	ttable.table = calloc(1 << index_bit_length, sizeof(ttable_entry));

	return ttable;
}

void free_ttable(ttable_t ttable)
{
	free(ttable.table);
}

ttable_entry *ttable_lookup(ttable_t ttable, uint64_t key)
{
	return ttable.table + (key >> (64-ttable.index_bit_length));
}

uint64_t gen_zobrist_key(board_t *b, int color)
{
	bitboard pb;
	int sq;
	uint64_t key;

	key = color * hash_number_side_to_move;

	pb = b->color[WHITE].pawns;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[piece_to_hash_piece[PAWN]][sq];
	}
	pb = b->color[WHITE].knights;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[piece_to_hash_piece[KNIGHT]][sq];
	}
	pb = b->color[WHITE].bishops;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[piece_to_hash_piece[BISHOP]][sq];
	}
	pb = b->color[WHITE].rooks;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[piece_to_hash_piece[ROOK]][sq];
	}
	pb = b->color[WHITE].queens;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[piece_to_hash_piece[QUEEN]][sq];
	}
	pb = b->color[WHITE].king;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[piece_to_hash_piece[KING]][sq];
	}

	pb = b->color[BLACK].pawns;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[6+piece_to_hash_piece[PAWN]][sq];
	}
	pb = b->color[BLACK].knights;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[6+piece_to_hash_piece[KNIGHT]][sq];
	}
	pb = b->color[BLACK].bishops;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[6+piece_to_hash_piece[BISHOP]][sq];
	}
	pb = b->color[BLACK].rooks;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[6+piece_to_hash_piece[ROOK]][sq];
	}
	pb = b->color[BLACK].queens;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[6+piece_to_hash_piece[QUEEN]][sq];
	}
	pb = b->color[BLACK].king;
	while (pb) {
		sq = last_one_64(pb);
		pb = BITCLEAR(pb, sq);
		key ^= hash_number_piece_position[6+piece_to_hash_piece[KING]][sq];
	}

	// get the file on which the side to move can perform an en passant
	if (b->color[!color].pawn_shadow)
		key ^= hash_number_enpassant[last_one_64(b->color[!color].pawn_shadow) % 8];

	key ^= hash_number_castle_rights[0] * b->kscastle[WHITE];
	key ^= hash_number_castle_rights[1] * b->qscastle[WHITE];
	key ^= hash_number_castle_rights[2] * b->kscastle[BLACK];
	key ^= hash_number_castle_rights[3] * b->qscastle[BLACK];

	return key;
}


